import { Injectable } from '@nestjs/common';
import { DbService } from '../../db/db.service';
import { RechargeOrder } from '@app/db/entity/RechargeOrder';
import { WechatService } from '@app/wechat';
import { CacheService } from '@app/cache';
import { User } from '@app/db/entity/User';
import { UserWalletLog } from '@app/db/entity/UserWalletLog';
import { DataSource, FindOptionsWhere, LessThan } from 'typeorm';
import { WALLET_LOG_TYPE } from 'constant/WALLET_LOG_TYPE';

@Injectable()
export class RechargeService {
    constructor(
        private readonly db:DbService,
        private readonly wechat:WechatService,
        private readonly cache:CacheService,
        private dataSource: DataSource,
    ) {}

    options() {
        return this.db.rechargeOption.find(null, null, null, null, {id : 'DESC'})
    }

    private generateOrderNo(userId:number) {
        const date = new Date()
        const time = Math.floor(date.getTime() / 1000) % 1000000
        const year = date.getFullYear() % 100
        const month = String(date.getMonth() + 1).padStart(2, '0')
        const day = String(date.getDate()).padStart(2, '0')
        return year + month + day + time + String(userId).padStart(10, '0')
    }


    async recharge(id:number, userId:number, clientSource:number, isWx:boolean) {
        const option = await this.db.rechargeOption.findOne({id : id}, {amount : true, reward : true})
        if(!option) return null

        const user = await this.db.user.findOne({id : userId}, {id : true, openid : true})

        let time = Math.floor(Date.now() / 1000)
        const orderNo = this.generateOrderNo(userId)
        let order:{[key in keyof RechargeOrder]? : RechargeOrder[key]} = {
            orderNo : orderNo,
            amount : option.amount,
            rewardAmount : option.reward,
            payChannel : clientSource,
            userId : userId,
            
            createTime : time
        }


        const res = await this.db.rechargeOrder.insert(order)
        if(res != null && res.affectedRows > 0) {
            const _id = res.rows[0][this.db.rechargeOrder.primaryKey]
            const notifyUrl = 'recharge/notify/'

            return this.wechat.pay(Number(option.amount), orderNo, user.openid, '充值', notifyUrl + 'wx/' + _id)
        }

        return null

        

        

        // if(isWx) {
        //     return this.wechat.pay(amount, orderNo, openid, '充值', notifyUrl + 'wx/' + _id)
        //     //await this.wechat.pay(amount, , openid, '充值')
        // }else {
        //     return this.alipay.createTrade(amount, orderNo, openid, '充值', notifyUrl + 'ali/' + _id)
        // }
    }


    async notify(id:number, data, isWx:boolean) {
        const lockKey= 'p:n:l' + id
        const lock = await this.cache.set(lockKey, '1', 20, 'NX')
        if(!lock) return false

        
        const order = await this.db.rechargeOrder.findOne({id : id}, {orderNo : true, userId : true, status : true, amount : true, rewardAmount : true})
        if(!order || order.status != 0) {
            return true
        }



        let tradeNo = ''

        if(isWx) {
            try {
                const res = await this.wechat.decipher_gcm(data.ciphertext, data.associated_data, data.nonce)
                if(res.trade_state != 'SUCCESS' || res.out_trade_no != order.orderNo || res.amount.total != (Number(order.amount) * 100)) return false

                tradeNo = res.transaction_id

            } catch (error) {
                this.cache.del(lockKey)
                console.log(error)
                return false
            } 
           
        }else { //支付宝
            // try {
            //     if(!this.alipay.checkNotifySign(data)) return false

            //     if(data.out_trade_no != temp.orderNo || data.total_amount != temp.data.amount) return false

            //     tradeNo = data.trade_no

            // } catch (error) {
            //     this.cache.del(lockKey)
            //     console.log(error)
            //     return false
            // }
        }

        const time = Math.floor(Date.now() / 1000)
      

        

        const queryRunner = await this.dataSource.createQueryRunner()
        await queryRunner.connect();
        await queryRunner.startTransaction();

        try {
            let user = await queryRunner.manager.findOne<User>(User, {
                where : {id : order.userId},
                select : ['amount', 'rewardAmount'],
                lock : {
                    mode : 'pessimistic_read'
                }
            })



            //用户钱包更新
            let updateData:{amount:number, rewardAmount?:number} = {
                amount : (Number(user.amount) + Number(order.amount))
            }

            //钱包记录
            const log:{[key in keyof UserWalletLog]? : UserWalletLog[key]} = {
                amount : order.amount,
                beforeAmount : String(user.amount),
                beforeRewardAmount : String(user.rewardAmount),
                afterAmount : updateData.amount.toFixed(2),

                userId : order.userId,
                createTime : time,
                type : WALLET_LOG_TYPE.RECHARGE
            }

            const rewardAmount = Number(order.rewardAmount)
            
            if(rewardAmount > 0) {
                updateData.rewardAmount = (Number(user.rewardAmount) + rewardAmount)

                log.rewardAmount = order.rewardAmount
                log.afterRewardAmount = updateData.rewardAmount.toFixed(2)
                
            }
            
            //更改用户余额
            const r1 = await queryRunner.manager.update<User>(User, { id : order.userId }, updateData)
            if(r1.affected < 1) {
                await queryRunner.rollbackTransaction();
                return false
            }

            

            //更新充值订单
            const r2 = await queryRunner.manager.update<RechargeOrder>(RechargeOrder, { id : id }, {
                status : 1,
                tradeNo : tradeNo,
                payTime : time
            })
            if(r2.affected < 1) {
                await queryRunner.rollbackTransaction();
                return false
            }


            log.relationId = id

            //加入钱包记录
            const r3 = await queryRunner.manager.insert<UserWalletLog>(UserWalletLog, log)
            if(!r3.raw || r3.raw.affectedRows < 1) {
                await queryRunner.rollbackTransaction();
                return false
            }

        
            await queryRunner.commitTransaction()

            return true
            
        } catch (error) {
            console.log(error)
            await queryRunner.rollbackTransaction();

            return false
        } finally {
            this.cache.del(lockKey)
            await queryRunner.release();
        }
        
    }


    async page(query:Pagination, userId:number) {
        let where:FindOptionsWhere<RechargeOrder> = { userId }
        const lastId = query.lastKey || 0
        const pageSize = query.pageSize || 20

        if(lastId > 0) {
            where.id = LessThan(lastId)
        }

        let rows:RechargeOrder[] = await this.db.rechargeOrder.getRepository().find({
            where,
            skip : 0, 
            take : pageSize, 
            order : {id : 'DESC'}
        })

        return rows
    }

}
